﻿/************************************************************************

   Extended Silverlight Toolkit

   Copyright (C) 2010-2012 Xceed Software Inc.

   This program is provided to you under the terms of the Microsoft Public
   License (Ms-PL) as published at http://extendedsilverlight.codeplex.com/license

   Please purchase a commercial version of this toolkit if you are using
   it in a commercial product. The commercial versions support the project,
   and also include more features for each control, priority support,
   source code, updates, removed watermark, and a proprietary license.

   Visit http://xceed.com and follow @datagrid on Twitter.

  **********************************************************************/

using System;
using System.Collections.Generic;
using System.Linq;

namespace Xceed.Silverlight.Data
{
  internal sealed class FilterExpressionSimplifier : FilterExpressionVisitor
  {
    #region Constructor

    internal FilterExpressionSimplifier( FilterExpressionBase expression )
      : base( expression )
    {
    }

    #endregion

    protected override void Initialize()
    {
      m_result = null;
    }

    protected internal override void Visit( FilterExpression expression )
    {
      m_result = expression;
    }

    protected internal override void Visit( AndFilterExpression expression )
    {
      ICollection<FilterExpressionBase> childExpressions;

      // No simplification has been done.
      if( !this.TryGetNewChildExpressions( expression, out childExpressions ) )
      {
        m_result = expression;
      }
      // A FilterExpressionOperator is valid only if there are at least 
      // two child expressions.
      else if( childExpressions.Count >= 2 )
      {
        m_result = new AndFilterExpression( childExpressions );
      }
      else
      {
        m_result = childExpressions.Single();
      }
    }

    protected internal override void Visit( OrFilterExpression expression )
    {
      ICollection<FilterExpressionBase> childExpressions;

      // No simplification has been done.
      if( !this.TryGetNewChildExpressions( expression, out childExpressions ) )
      {
        m_result = expression;
      }
      // A FilterExpressionOperator is valid only if there are at least 
      // two child expressions.
      else if( childExpressions.Count >= 2 )
      {
        m_result = new OrFilterExpression( childExpressions );
      }
      else
      {
        m_result = childExpressions.Single();
      }
    }

    protected internal override void Visit( NotFilterExpression expression )
    {
      NotFilterExpression childExpression = expression.ChildExpression as NotFilterExpression;

      // Two consecutive "Not" may be simplified.
      if( childExpression != null )
      {
        this.Visit( childExpression.ChildExpression );
      }
      // Try to simplify the child expression itself.
      else
      {
        this.Visit( expression.ChildExpression );

        // No simplification has been done.
        if( m_result == expression.ChildExpression )
        {
          m_result = expression;
        }
        else
        {
          m_result = new NotFilterExpression( m_result );
        }
      }
    }

    internal FilterExpressionBase GetResult()
    {
      this.Visit();

      return m_result;
    }

    private bool TryGetNewChildExpressions( FilterExpressionOperator expression, out ICollection<FilterExpressionBase> childExpressions )
    {
      Type expressionType = expression.GetType();
      List<FilterExpressionBase> newChildExpressions = new List<FilterExpressionBase>( expression.ChildExpressions.Count );
      bool hasChanged = false;

      foreach( FilterExpressionBase childExpression in expression.ChildExpressions )
      {
        this.Visit( childExpression );

        FilterExpressionBase newChildExpression = m_result;

        // The child expression is of the same operator type.  The children of the child
        // expression may be promoted to this level.
        if( expressionType == newChildExpression.GetType() )
        {
          FilterExpressionOperator childOperator = ( FilterExpressionOperator )newChildExpression;

          foreach( FilterExpressionBase promotedChildExpression in childOperator.ChildExpressions )
          {
            if( !newChildExpressions.Contains( promotedChildExpression ) )
            {
              newChildExpressions.Add( promotedChildExpression );
            }
          }

          hasChanged = true;
        }
        // Make sure the child expression isn't a duplicate of an expression we
        // have already managed.
        else if( !newChildExpressions.Contains( newChildExpression ) )
        {
          newChildExpressions.Add( newChildExpression );

          hasChanged |= ( newChildExpression != childExpression );
        }
        // The child expression is a duplicate.  We may ignore it.
        else
        {
          hasChanged = true;
        }
      }

      if( hasChanged )
      {
        childExpressions = newChildExpressions;
      }
      else
      {
        childExpressions = expression.ChildExpressions;
      }

      return hasChanged;
    }

    #region Private Fields

    private FilterExpressionBase m_result;

    #endregion
  }
}
